package commonfunctions;

import java.util.HashMap;

/**
 * I created this object when I tried to create a two-dimensional array containing all the weights 
 * from one observation to another. Because this required a contiguous block of memory of roughly 10 
 * megabytes for 1000 observations, the Java heap space quickly ran out. However, most values are 0.0, 
 * so I came up with this data structure. 
 * 
 * Essentially, it is a hashmap of hashmaps, and when a value is set as zero it is simply removed, and 
 * when a value that doesn't exist is asked for, zero is returned. Thus, it runs at nearly the speed 
 * of a two-dimensional array, without the memory requirements.
 * 
 * It also can be used to store booleans, returning true and false instead of 1.0 and 0.0. 
 * 
 * @author Trevor Terris
 *
 */
public class Matrix 
{
	private HashMap<Integer, HashMap<Integer, Double>> myHash;
	private int mySize = 0;
	/**
	 * initializes the Matrix. Requires a size.
	 * 
	 * @param size
	 */
	public Matrix(int size)
	{
		mySize = size;
		myHash = new HashMap<Integer, HashMap<Integer, Double>>();
	}
	/**
	 * The Matrix can be initialized from another Matrix.
	 * 
	 * @param m
	 */
	public Matrix(Matrix m)
	{
		mySize = m.length();
		myHash = new HashMap<Integer, HashMap<Integer, Double>>();
		for (int i = 0; i < m.length(); i++)
		{
			for (int j = 0; j < m.length(); j++)
			{
				set(m.get(i, j), i, j);
			}
		}
	}
	/**
	 * returns the double value at a given row and column.
	 * 
	 * @param row
	 * @param col
	 * @return
	 */
	public double get(int row, int col)
	{
		if (myHash.containsKey(row) && myHash.get(row).containsKey(col))
			return myHash.get(row).get(col);
		return 0.0;
	}
	/**
	 * returns the boolean value that is stored.
	 * 
	 * @param row
	 * @param col
	 * @return
	 */
	public boolean getBoolean(int row, int col)
	{
		return (get(row, col) != 0.0);
	}
	/**
	 * returns the size of the matrix.
	 * 
	 * @return
	 */
	public int length()
	{
		return mySize;
	}
	/**
	 * Stores the value num at an entry in the Matrix.
	 * 
	 * @param num
	 * @param row
	 * @param col
	 */
	public void set(double num, int row, int col)
	{
		if (num == 0.0)
		{
			if (myHash.containsKey(row) && myHash.get(row).containsKey(col))
				myHash.get(row).remove(col);
		}
		else
		{
			if (!myHash.containsKey(row))
				myHash.put(row, new HashMap<Integer, Double>());
			myHash.get(row).put(col, num);
		}
	}
	/**
	 * Stores the boolean value at an entry in the Matrix.
	 * 
	 * @param val
	 * @param row
	 * @param col
	 */
	public void set(boolean val, int row, int col)
	{
		set((val)?1.0:0.0, row, col);
	}
	/**
	 * returns an entire row in an array. Not as efficient as a two-dimensional array.
	 * 
	 * @param row
	 * @return
	 */
	public double[] singleRow(int row)
	{
		double[] output = new double[mySize];
		for (int i = 0; i < mySize; i++)
		{
			output[i] = (myHash.containsKey(row) && myHash.get(row).containsKey(i))?myHash.get(row).get(i):0.0;
		}
		return output;
	}
	/**
	 * returns an entire row of boolean values in an array.
	 * 
	 * @param row
	 * @return
	 */
	public boolean[] singelBooleanRow(int row)
	{
		double[] doubleRow = singleRow(row);
		boolean[] output = new boolean[mySize];
		for(int i = 0; i < mySize; i++)
		{
			output[i] = (doubleRow[i] == 1.0);
		}
		return output;
	}
	/**
	 * Returns an entire column.
	 * 
	 * @param col
	 * @return
	 */
	public double[] singleCol(int col)
	{
		double[] output = new double[mySize];
		for (int i = 0; i < mySize; i++)
		{
			output[i] = (myHash.containsKey(i) && myHash.get(i).containsKey(col))?myHash.get(i).get(col):0.0;
		}
		return output;
	}
	/**
	 * Returns an entire column of boolean values.
	 * 
	 * @param col
	 * @return
	 */
	public boolean[] singelBooleanCol(int col)
	{
		double[] doubleCol = singleCol(col);
		boolean[] output = new boolean[mySize];
		for(int i = 0; i < mySize; i++)
		{
			output[i] = (doubleCol[i] == 1.0);
		}
		return output;
	}
}
